﻿bob.ns.setNs('bob.collections.extensions.tests', {

    TestExtensions: function () {

        this.test_equals = function () {

            var array = [1, 2, 3];
            var other = [1, 2, 3];

            bob.assert.isTrue(bob.collections.extensions.equals.call(array, other), '1');
            other.splice(2, 0, 4);
            bob.assert.isFalse(bob.collections.extensions.equals.call(array, other), '2');
            other.splice(2, 1);
            bob.assert.isTrue(bob.collections.extensions.equals.call(array, other), '3');

        };

        this.test_clone = function () {

            //full-clone.
            var array = [1, 2, 3];
            var other = bob.collections.extensions.clone.call(array);
            bob.assert.isTrue(bob.collections.extensions.equals.call(array, other));

            //part-clone.
            other = bob.collections.extensions.clone.call(array, 1);
            bob.assert.isTrue(bob.collections.extensions.equals.call([2, 3], other));

            //clone-to-destination.
            other = [0];
            bob.collections.extensions.clone.call(array, 0, 1, other);
            bob.assert.isTrue(bob.collections.extensions.equals.call([0, 1, 2], other));

            //clone object.
            var obj = { a: 1 };
            var other = bob.collections.extensions.clone.call(obj);
            bob.assert.isTrue(bob.collections.extensions.equals.call(obj, other));
        };

        this.test_cloneWithCondFn = function () {

            //full-clone.
            var array = [1, 2, 3];
            var other = bob.collections.extensions.clone.call(array, function (index, item) { return true; });
            bob.assert.isTrue(bob.collections.extensions.equals.call(array, other));

            //part-clone.
            other = bob.collections.extensions.clone.call(array, function (index, item) { return index >= 1; });
            bob.assert.isTrue(bob.collections.extensions.equals.call([2, 3], other));

            //clone-to-destination.
            other = [0];
            bob.collections.extensions.clone.call(array, function (index, item) { return bob.collections.extensions.indexOf.call([0, 1], true, index) >= 0; }, other);
            bob.assert.isTrue(bob.collections.extensions.equals.call([0, 1, 2], other));
        };

        this.test_any = function () {
            var array = [1, null, 2, undefined, 3];
            bob.assert.isTrue(bob.collections.extensions.any.call(array, function () { return this > 2; }));
            bob.assert.isFalse(bob.collections.extensions.any.call(array, function () { return this > 3; }));
            bob.assert.isTrue(bob.collections.extensions.any.call(array, function (i) {
                return bob.utils.isUndefined(array[i]);
            }));

            array = [1, 2, 3];
            bob.assert.isFalse(bob.collections.extensions.any.call(array, function (i) {
                return bob.utils.isUndefined(array[i]);
            }));
        };

        this.test_all = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.all.call([], null); });

            var array = [0, 1, 2, 3];
            bob.assert.isFalse(bob.collections.extensions.all.call(array, function (index, item) {
                return item > 0;
            }));

            array.splice(0, 1);
            bob.assert.isTrue(bob.collections.extensions.all.call(array, function (index, item) { return item > 0; }));
            bob.assert.isTrue(bob.collections.extensions.all.call(array, function (index, item) { return item == (index + 1); }));

        };

        this.test_forEach = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.forEach.call([], null); });

            var array = [1, 2, 3];
            bob.collections.extensions.forEach.call(array, function (index, item) { array[index] = item + 1; });
            bob.assert.isTrue(bob.collections.extensions.equals.call(array, [2, 3, 4]));
        };

        this.test_forEach_objectIndexer = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.forEach.call([], null); });

            var obj = { a: 1, b: 2, c: 3 };
            var arr = [{ name: 'a', value: 1 }, { name: 'b', value: 2 }, { name: 'c', value: 3}];
            var i = 0;
            bob.collections.extensions.forEach.call(obj, function (index, item) {
                bob.assert.equals(arr[i].name, index);
                bob.assert.equals(arr[i].value, item);
                i++;
            });
        };

        this.test_firstOrDefault = function () {

            var array = [1, 2, 3];
            var item = bob.collections.extensions.firstOrDefault.call(array, function (index, item) { return item == 1; }, 5);
            bob.assert.equals(1, item, '2');

            item = bob.collections.extensions.firstOrDefault.call(array, function (index, item) { return item == 2; }, 5);
            bob.assert.equals(2, item, '3');

            item = bob.collections.extensions.firstOrDefault.call(array, function (index, item) { return item == 3; }, 5);
            bob.assert.equals(3, item, '4');

            item = bob.collections.extensions.firstOrDefault.call(array, function (index, item) { return item == 4; }, 5);
            bob.assert.equals(5, item, '5');

            //get just first item.
            var array = [1, 2, 3];
            var item = bob.collections.extensions.firstOrDefault.call(array);
            bob.assert.equals(1, item, '6');
        };

        this.test_lastOrDefault = function () {

            var array = [1, 2, 3];
            var item = bob.collections.extensions.lastOrDefault.call(array, function (index, item) { return item == 1; }, 5);
            bob.assert.equals(1, item, '2');

            item = bob.collections.extensions.lastOrDefault.call(array, function (index, item) { return item == 2; }, 5);
            bob.assert.equals(2, item, '3');

            item = bob.collections.extensions.lastOrDefault.call(array, function (index, item) { return item == 3; }, 5);
            bob.assert.equals(3, item, '4');

            item = bob.collections.extensions.lastOrDefault.call(array, function (index, item) { return item == 4; }, 5);
            bob.assert.equals(5, item, '5');

            item = bob.collections.extensions.lastOrDefault.call(array, function (index, item) { return item < 3; }, 5);
            bob.assert.equals(2, item, '5');

            //get just last item.
            var array = [1, 2, 3];
            var item = bob.collections.extensions.lastOrDefault.call(array);
            bob.assert.equals(3, item, '6');
        };

        this.test_select = function () {
            //empty array.
            var arr = [];
            var res = bob.collections.extensions.select.call(arr, function (index, item) { return item + 1; });
            bob.assert.isTrue(bob.utils.isArray(res), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(arr, res), '5');

            //regular array.
            var arr = [1, 2, 3];
            var res = bob.collections.extensions.select.call(arr, function (index, item) { return item + 1; });
            bob.assert.isTrue(bob.utils.isArray(res), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call([2, 3, 4], res), '7');
        };

        this.test_where = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.where.call([], null); }, '1');

            //empty array.
            var arr = [];
            var res = bob.collections.extensions.where.call(arr, function (index, item) { return index == item; });
            bob.assert.isTrue(bob.utils.isArray(res), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(arr, res), '5');

            //regular array.
            var arr = [1, 1, 2, 3, 3, 10, 11, 12];
            var res = bob.collections.extensions.where.call(arr, function (index, item) { return index == item; });
            bob.assert.isTrue(bob.utils.isArray(res), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3], res), '7');
        };

        this.test_recursiveWhere = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.recursiveWhere.call([], null, null); }, '1');
            //empty array.
            var arr = [];
            var res = bob.collections.extensions.recursiveWhere.call(arr, function (index, item) { return index == item; }, function () { return []; });
            bob.assert.isTrue(bob.utils.isArray(res), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(arr, res), '5');
            //regular array.
            var arr = [1, 1, 2, 3, 3, 10, 11, 12];
            var res = bob.collections.extensions.recursiveWhere.call(arr, function (index, item) { return index == item; }, function () { return []; });
            bob.assert.isTrue(bob.utils.isArray(res), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3], res), '7');

            //composite objects.
            var arr = [{ value: 1 }, { value: 2 }, { value: 2 }, { value: 3 }, { value: 3 }, { value: 10 }, { value: 11 }, { value: 12}];
            var res = bob.collections.extensions.recursiveWhere.call(arr, function (index, item) { return item.value == 11; }, function () { return []; });
            bob.assert.isTrue(bob.utils.isArray(res), '8.1');
            bob.assert.equals(1, res.length, '8.2');

            var arr = [{ value: 1 }, { value: 2 }, { value: 2 }, { value: 3 }, { value: 3 }, { value: 10 }, { value: 12 }, { value: 12}];
            var res = bob.collections.extensions.recursiveWhere.call(arr, function (index, item) { return item.value == 11; }, function () { return []; });
            bob.assert.isTrue(bob.utils.isArray(res), '9.1');
            bob.assert.equals(0, res.length, '9.2');

            var arr = [{ value: 1 }, { value: 2, items: [{ value: 11 }] }, { value: 2 }, { value: 3 }, { value: 3 }, { value: 10 }, { value: 12 }, { value: 12}];
            var res = bob.collections.extensions.recursiveWhere.call(arr, function (index, item) { return item.value == 11; }, function (index, item) { return item.items || []; });
            bob.assert.isTrue(bob.utils.isArray(res), '10.1');
            bob.assert.equals(1, res.length, '10.2');

            var arr = [{ value: 1 }, { value: 2, items: [{ value: 11}] }, { value: 2 }, { value: 3 }, { value: 3 }, { value: 11 }, { value: 12 }, { value: 12}];
            var res = bob.collections.extensions.recursiveWhere.call(arr, function (index, item) { return item.value == 11; }, function (index, item) { return item.items || []; });
            bob.assert.isTrue(bob.utils.isArray(res), '10.1');
            bob.assert.equals(2, res.length, '10.2');
        };

        this.test_except_withFn = function () {

            //empty array.
            var arr = [];
            var res = bob.collections.extensions.except.call(arr, function (index, item) { return index == item; });
            bob.assert.isTrue(bob.utils.isArray(res), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(arr, res), '5');

            //regular array.
            var arr = [1, 1, 2, 3, 3, 10, 11, 12];
            var res = bob.collections.extensions.except.call(arr, function (index, item) { return index == item; });
            bob.assert.isTrue(bob.utils.isArray(res), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 3, 10, 11, 12], res), '7');

            //string.
            var str = 'boAb';
            var res = bob.collections.extensions.except.call(str, function (index, item) { return item == 'A' });
            bob.assert.isString(res, '8');
            bob.assert.equals('bob', res, '9');
        };

        this.test_except_withIndex = function () {

            //empty array.
            var arr = [];
            var res = bob.collections.extensions.except.call(arr, 0);
            bob.assert.isTrue(bob.utils.isArray(res), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(arr, res), '5');

            //regular array.
            var arr = [0, 1, 2, 3];
            var res = bob.collections.extensions.except.call(arr, 0);
            bob.assert.isTrue(bob.utils.isArray(res), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3], res), '7');

            var arr = [1, 2, 3];
            var res = bob.collections.extensions.except.call(arr, 1);
            bob.assert.isTrue(bob.utils.isArray(res), '8');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 3], res), '9');

            var arr = [1, 3];
            var res = bob.collections.extensions.except.call(arr, 1);
            bob.assert.isTrue(bob.utils.isArray(res), '10');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1], res), '11');

            var arr = [1];
            var res = bob.collections.extensions.except.call(arr, 0);
            bob.assert.isTrue(bob.utils.isArray(res), '12');
            bob.assert.isTrue(bob.collections.extensions.equals.call([], res), '13');
        };

        this.test_except_withItem = function () {

            //empty array.
            var arr = [];
            var res = bob.collections.extensions.except.call(arr, true, 1);
            bob.assert.isTrue(bob.utils.isArray(res), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(arr, res), '5');

            //regular array.
            var arr = [1, 1, 2, 3, 3, 10, 11, 12];
            var res = bob.collections.extensions.except.call(arr, true, 1);
            bob.assert.isTrue(bob.utils.isArray(res), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call([2, 3, 3, 10, 11, 12], res), '7');

            //string.
            var str = 'boAb';
            var res = bob.collections.extensions.except.call(str, true, 'A');
            bob.assert.isString(res, '8');
            bob.assert.equals('bob', res, '9');
        };

        this.test_atIndex = function () {
            //empty array.
            var arr = [];
            bob.assert.throwsError(function () { bob.collections.extensions.atIndex.call(arr, 0); }, '3');

            //regular array.
            var arr = [10, 20, 30];
            var item = bob.collections.extensions.atIndex.call(arr, 0);
            bob.assert.equals(10, item, '4');

            var item = bob.collections.extensions.atIndex.call(arr, 1);
            bob.assert.equals(20, item, '5');

            var item = bob.collections.extensions.atIndex.call(arr, 2);
            bob.assert.equals(30, item, '6');

            bob.assert.throwsError(function () { bob.collections.extensions.atIndex.call(arr, 3); }, '7');
        };

        this.test_combine = function () {

            //array appended.
            var arr = [1, 2, 3];
            var arr2 = [4, 5];
            var combined = bob.collections.extensions.combine.call(arr, arr2);
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3], arr), '0');
            bob.assert.isTrue(bob.collections.extensions.equals.call([4, 5], arr2), '1');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3, 4, 5], combined), '2');

            //array merged.
            var arr = [1, 2, 3];
            var arr2 = [3, 4, 5];
            var combined = bob.collections.extensions.combine.call(arr, arr2);
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3], arr), '3');
            bob.assert.isTrue(bob.collections.extensions.equals.call([3, 4, 5], arr2), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call([1, 2, 3, 3, 4, 5], combined), '5');

            //object appended.
            var obj = { name: 'bob', age: 20 };
            var obj2 = { city: 'miami' };
            var combined = bob.collections.extensions.combine.call(obj, obj2);
            bob.assert.isTrue(bob.collections.extensions.equals.call({ name: 'bob', age: 20 }, obj), '6');
            bob.assert.isTrue(bob.collections.extensions.equals.call({ city: 'miami' }, obj2), '7');
            bob.assert.isTrue(bob.collections.extensions.equals.call({ name: 'bob', age: 20, city: 'miami' }, combined), '7');

            //object merged.
            var obj = { name: 'bob', age: 20 };
            var obj2 = { age: 21, city: 'miami' };
            var combined = bob.collections.extensions.combine.call(obj, obj2);
            bob.assert.isTrue(bob.collections.extensions.equals.call({ name: 'bob', age: 20 }, obj), '8');
            bob.assert.isTrue(bob.collections.extensions.equals.call({ age: 21, city: 'miami' }, obj2), '9');
            bob.assert.isTrue(bob.collections.extensions.equals.call({ name: 'bob', age: 21, city: 'miami' }, combined), '10');
        };

        this.test_count = function () {

            //empty array.
            var arr = [];
            var count = bob.collections.extensions.count.call(arr);
            bob.assert.equals(0, count, '3');

            //regular array.
            var arr = [1, 2];
            var count = bob.collections.extensions.count.call(arr);
            bob.assert.equals(2, count, '4');

            //condition fn.
            var arr = [1, 2, 3];
            var count = bob.collections.extensions.count.call(arr, function (index, item) { return item >= 2; });
            bob.assert.equals(2, count, '5');
        };

        this.test_toObject = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.toObject.call([], null); }, '1');

            var arr = ['name', 'Bob', 'version', 1];
            var obj = bob.collections.extensions.toObject.call(arr, function (index, item) {
                return index % 2 ? arr[index - 1] : item;
            }, function (index, item) {
                return index % 2 ? item : undefined;
            });

            bob.assert.isTrue(bob.collections.extensions.equals.call({ name: 'Bob', version: 1 }, obj), '4');
        };

        this.test_toArray = function () {

            var obj = { name: 'Bob', version: 1 };
            var arr = bob.collections.extensions.toArray.call(obj, function (prop, value) {
                return value;
            });

            bob.assert.isTrue(bob.collections.extensions.equals.call(['Bob', 1], arr), '3');
        };

        this.test_groupBy = function () {

            var arr = ['1', '2', '3', '10', '20', '21', '22', '30', '31', '32', '33'];
            var obj = bob.collections.extensions.groupBy.call(arr, function (index, item) {
                return item.substr(0, 1);
            }, function (index, item) {
                return item * 1;
            });

            bob.assert.isObject(obj, '2');
            var objExpected = { '1': [1, 10], '2': [2, 20, 21, 22], '3': [3, 30, 31, 32, 33] };
            bob.assert.isTrue(bob.collections.extensions.equals.call(objExpected, obj, function (one, two, oneIndex, twoIndex) {
                return oneIndex == twoIndex;
            }), '3');
            bob.assert.isTrue(bob.collections.extensions.equals.call(objExpected['1'], obj['1']), '4');
            bob.assert.isTrue(bob.collections.extensions.equals.call(objExpected['2'], obj['2']), '5');
            bob.assert.isTrue(bob.collections.extensions.equals.call(objExpected['3'], obj['3']), '4');

        };

        this.test_aggregate = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.aggregate.call([], null); }, '1');

            var arr = [{ n: 1 }, { n: 2 }, { n: 3}];
            var result = 0;
            var sum = bob.collections.extensions.aggregate.call(arr, function (index, item) {
                result += item.n;
            }, function () {
                return result;
            });

            bob.assert.equals(result, sum, '2');
        };

        this.test_sum = function () {

            var arr = [{ n: 1 }, { n: 2 }, { n: 3}];
            var sum = bob.collections.extensions.sum.call(arr, function (index, item) { return item.n; });
            bob.assert.equals(1 + 2 + 3, sum, '2');

            //implicit function.
            var arr = [1, 2, 3, 4];
            var sum = bob.collections.extensions.sum.call(arr);
            bob.assert.equals(1 + 2 + 3 + 4, sum, '3');
        };

        this.test_avg = function () {

            var arr = [{ n: 1 }, { n: 2 }, { n: 3}];
            var avg = bob.collections.extensions.avg.call(arr, function (index, item) { return item.n; });
            bob.assert.equals((1 + 2 + 3) / 3, avg, '2');

            //implicit function.
            var arr = [1, 2, 3, 4];
            var avg = bob.collections.extensions.avg.call(arr);
            bob.assert.equals((1 + 2 + 3 + 4) / 4, avg, '3');
        };

        this.test_min = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.min.call([]); }, '1');

            var arr = [{ n: 1 }, { n: 2 }, { n: 3}];
            var min = bob.collections.extensions.min.call(arr, function (index, item) { return item.n; });
            bob.assert.equals(1, min, '2');

            //implicit function.
            var arr = [2, 3, 4];
            var min = bob.collections.extensions.min.call(arr);
            bob.assert.equals(2, min, '3');
        };

        this.test_max = function () {
            bob.assert.throwsError(function () { bob.collections.extensions.max.call([]); }, '1');

            var arr = [{ n: 1 }, { n: 2 }, { n: 3}];
            var max = bob.collections.extensions.max.call(arr, function (index, item) { return item.n; });
            bob.assert.equals(3, max, '2');

            //implicit function.
            var arr = [1, 2, 3, 4];
            var max = bob.collections.extensions.max.call(arr);
            bob.assert.equals(4, max, '3');
        };
    }

});